using System;
using System.Diagnostics;
using System.Collections.Generic;

namespace OpenRay
{
	public class ProceduralRefractivePhongMaterial : ProceduralReflectivePhongMaterial
	{
		private readonly IProceduralColor absorbance;
		private readonly List<IProceduralComponent> refractiveIndex = new List<IProceduralComponent>(2);
		
		public ProceduralRefractivePhongMaterial ( IList<IProceduralColor> a, IList<IProceduralColor> d, IList<IProceduralColor> s, IList<IProceduralColor> e, 
		                                		   IList<IProceduralComponent> sh, IList<IProceduralColor> r, IList<IProceduralComponent> i, IProceduralColor ab , IScene sc )
			: base( a, d, s, e, sh, r, sc )
		{
			Debug.Assert( i.Count == 2, "Incorrect number of refractive indices provided" );
			Debug.Assert( ab != null, "No absorbance provided" );
			
			refractiveIndex.AddRange( i );
			absorbance = ab;
		}
		
		protected Color RefractiveShade( Point worldHit, Point ModelHit, Vector normalizedViewer, Vector surfaceNormal, Material.Face f, uint depth )
		{
			float nQuotient = ( f == Material.Face.Front ) ?  refractiveIndex[0].Evaluate(ModelHit) / refractiveIndex[1].Evaluate(ModelHit) :
				        									  refractiveIndex[1].Evaluate(ModelHit) / refractiveIndex[0].Evaluate(ModelHit);
			
			Vector refracted = ( f == Material.Face.Front ) ? Vector.Refract( normalizedViewer, surfaceNormal, nQuotient ) :
															  Vector.Refract( normalizedViewer, -surfaceNormal, nQuotient );
			
			if ( refracted.Length() == 0f ) //Total Internal Reflection
				return new Color( 0f, 0f, 0f, 1f );
			
			Ray r = new Ray( worldHit, refracted );
			IList<PrimitiveHit> hits = scene.Trace(r, depth);
			
			if ( hits.Count == 0 )
				return new Color( 0f, 0f, 0f, 1f );

			return ( f == Material.Face.Front ) ? hits[0].Shade(r) * absorbance.Evaluate(ModelHit) * (float)Math.Exp( -0.15f * hits[0].Distance ) : 
												  hits[0].Shade(r);
		}
		
		public override Color Shade( Point worldHit, Point ModelHit, Vector normalizedViewer, Vector surfaceNormal, IList<Light> lights, uint depth ) {
			Debug.Assert( surfaceNormal.IsNormal(), "RefractivePhongMaterial.Shade: Viewer is not normalized" );
				
			Material.Face f = FaceHit(normalizedViewer, surfaceNormal);
			
			return RefractiveShade( worldHit, ModelHit, normalizedViewer, surfaceNormal, f, depth );// +
				   //ReflectiveMaterialShade(worldHit, ModelHit, normalizedViewer, surfaceNormal, f, depth ) +
				   //PhongMaterialShade( worldHit, ModelHit, normalizedViewer, surfaceNormal, lights, f );
		}
		
		public override Color Shade( Point worldHit, Point ModelHit, Vector normalizedViewer, Vector surfaceNormal, IList<Light> lights, Material.Face f, uint depth ) {			
			return RefractiveShade( worldHit, ModelHit, normalizedViewer, surfaceNormal, f, depth );// +
				   //ReflectiveMaterialShade(worldHit, ModelHit, normalizedViewer, surfaceNormal, f, depth ) +
				   //PhongMaterialShade( worldHit, ModelHit, normalizedViewer, surfaceNormal, lights, f );
		}
	}
}

